#include <Math/Vector2.hpp>
#include <Math/Vector3.hpp>
#include <Math/Vector4.hpp>
#include <Math/Math.hpp>
#include <UnitTest++.h>

namespace zzz{
  struct ClassA{
    int x;
    void operator =(const ClassA &other) {x=other.x+1;}
  };
  template<>
  class IOObject<ClassA> {
  public:
    static void CopyData(ClassA* dst, const ClassA* src, zsize size) {
      memcpy(dst,src,sizeof(ClassA)*size);
    }
  };
  struct ClassB {
    int x;
    void operator =(const ClassB &other) {x=other.x+1;}
  };
}

using namespace zzz;

SUITE(VectorTest)
{
  TEST(Constructor) 
  {
    CHECK_EQUAL(sizeof(Vector3f),sizeof(float)*3);

    Vector3f a(1,2,3);
    CHECK_EQUAL(a.x(),1);
    CHECK_EQUAL(a.y(),2);
    CHECK_EQUAL(a.z(),3);

    Vector3f b(a);
    CHECK_EQUAL(b,a);

    int vi[]={5,6,7};
    float vf[]={8,9,10};
    Vector3f c(vi),d(vf);
    CHECK_ARRAY_EQUAL(vi,c.Data(),3);
    CHECK_ARRAY_EQUAL(vf,d.Data(),3);
  }

  TEST(Assign_RawCopy)
  {
    ClassB aa;
    aa.x=0;

    Vector<5,ClassB> a,b;
    a[0]=aa;
    CHECK_EQUAL(a[0].x,1);
    b[0].x=1;
    a=b;  //will call operator=
    CHECK_EQUAL(a[0].x,2);
    a.RawCopy(b);  //force raw copy
    CHECK_EQUAL(a[0].x,1);
  }

  TEST(SmartAssignCopy)
  {
    ClassA aa;
    aa.x=0;

    Vector<2,Vector<2,ClassA> > a,b;
    a[0][0].x=0;
    a[0][1].x=1;
    a[1][0].x=2;
    a[1][1].x=3;
    b=a;
    CHECK_EQUAL(0,b[0][0].x);
    CHECK_EQUAL(1,b[0][1].x);
    CHECK_EQUAL(2,b[1][0].x);
    CHECK_EQUAL(3,b[1][1].x);
  }

  TEST(Math)
  {
    Vector3f a(1,2,3);
    CHECK_EQUAL(a,Vector3f(1,2,3));
    a=-a;
    CHECK_EQUAL(a,Vector3f(-1,-2,-3));
    a=a+a;
    CHECK_EQUAL(a,Vector3f(-2,-4,-6));
    a=a*a;
    CHECK_EQUAL(a,Vector3f(4,16,36));
    Vector3f b=a.Normalized();
    CHECK_CLOSE(1,b.Len(),0.1);
    a.Normalize();
    CHECK_EQUAL(a,b);
    CHECK_CLOSE(a.DistTo(b),0,0);
    Vector3f c=a.RandomPerpendicularUnitVec();
    CHECK_CLOSE(c.Len(),1,EPS);
    CHECK_CLOSE(a.Dot(c),0,EPS);
  }

  TEST(CompareTest)
  {
    Vector3f a(1,2,3), b(1,2,3), c(1,3,2), d(-3,1,2);
    CHECK(a==b);
    CHECK(a<c);
    CHECK(a>d);
    CHECK_EQUAL(3,a.Max());
    CHECK_EQUAL(1,a.Min());
    CHECK_EQUAL(0,a.MinPos());
    CHECK_EQUAL(2,a.MaxPos());
    CHECK_EQUAL(-3,d.Min());
    CHECK_EQUAL(1,d.AbsMin());
    CHECK_EQUAL(0,d.AbsMaxPos());
    a.KeepMax(c);
    CHECK_EQUAL(Vector3f(1,3,3), a);
    b.KeepMin(d);
    CHECK_EQUAL(Vector3f(-3,1,2), b);

  }

  TEST(STL)
  {
    Vector<6,float> x;
    for (zuint i=0; i<x.size(); i++)
      x[i]=i;
    for (Vector<6,float>::iterator vi=x.begin();vi!=x.end();vi++)
      CHECK_EQUAL(vi-x.begin(),*vi);

    Vector4d x1;
    for (Vector4d::reverse_iterator vi=x1.rbegin();vi!=x1.rend();vi++)
      *vi=vi-x1.rbegin();
    CHECK_EQUAL(6,accumulate(x1.begin(),x1.end(),0));

  }

  TEST(MetaDot)
  {
    Vector<6,float> x,y;
    for (size_t i=0; i<6; i++)
      x[i]=y[i]=i;
    CHECK_EQUAL(x.Dot(y),FastDot(x,y));
  }
}